source("function_import.R")
unable to open connection to X11 display ''

processing file: 00-project-overview.Rmd

  |                                                                       
  |                                                                 |   0%
  |                                                                       
  |......                                                           |   9%
  |                                                                       
  |............                                                     |  18%
  |                                                                       
  |..................                                               |  27%
  |                                                                       
  |........................                                         |  36%
  |                                                                       
  |..............................                                   |  45%
  |                                                                       
  |...................................                              |  55%
  |                                                                       
  |.........................................                        |  64%
  |                                                                       
  |...............................................                  |  73%
  |                                                                       
  |.....................................................            |  82%
  |                                                                       
  |...........................................................      |  91%
  |                                                                       
  |.................................................................| 100%
output file: 00-project-overview.R

unable to open connection to X11 display ''

processing file: 10-import-data.Rmd

  |                                                                       
  |                                                                 |   0%
  |                                                                       
  |.                                                                |   2%
  |                                                                       
  |..                                                               |   3%
  |                                                                       
  |...                                                              |   5%
  |                                                                       
  |....                                                             |   7%
  |                                                                       
  |.....                                                            |   8%
  |                                                                       
  |......                                                           |  10%
  |                                                                       
  |.......                                                          |  11%
  |                                                                       
  |.........                                                        |  13%
  |                                                                       
  |..........                                                       |  15%
  |                                                                       
  |...........                                                      |  16%
  |                                                                       
  |............                                                     |  18%
  |                                                                       
  |.............                                                    |  20%
  |                                                                       
  |..............                                                   |  21%
  |                                                                       
  |...............                                                  |  23%
  |                                                                       
  |................                                                 |  25%
  |                                                                       
  |.................                                                |  26%
  |                                                                       
  |..................                                               |  28%
  |                                                                       
  |...................                                              |  30%
  |                                                                       
  |....................                                             |  31%
  |                                                                       
  |.....................                                            |  33%
  |                                                                       
  |......................                                           |  34%
  |                                                                       
  |.......................                                          |  36%
  |                                                                       
  |.........................                                        |  38%
  |                                                                       
  |..........................                                       |  39%
  |                                                                       
  |...........................                                      |  41%
  |                                                                       
  |............................                                     |  43%
  |                                                                       
  |.............................                                    |  44%
  |                                                                       
  |..............................                                   |  46%
  |                                                                       
  |...............................                                  |  48%
  |                                                                       
  |................................                                 |  49%
  |                                                                       
  |.................................                                |  51%
  |                                                                       
  |..................................                               |  52%
  |                                                                       
  |...................................                              |  54%
  |                                                                       
  |....................................                             |  56%
  |                                                                       
  |.....................................                            |  57%
  |                                                                       
  |......................................                           |  59%
  |                                                                       
  |.......................................                          |  61%
  |                                                                       
  |........................................                         |  62%
  |                                                                       
  |..........................................                       |  64%
  |                                                                       
  |...........................................                      |  66%
  |                                                                       
  |............................................                     |  67%
  |                                                                       
  |.............................................                    |  69%
  |                                                                       
  |..............................................                   |  70%
  |                                                                       
  |...............................................                  |  72%
  |                                                                       
  |................................................                 |  74%
  |                                                                       
  |.................................................                |  75%
  |                                                                       
  |..................................................               |  77%
  |                                                                       
  |...................................................              |  79%
  |                                                                       
  |....................................................             |  80%
  |                                                                       
  |.....................................................            |  82%
  |                                                                       
  |......................................................           |  84%
  |                                                                       
  |.......................................................          |  85%
  |                                                                       
  |........................................................         |  87%
  |                                                                       
  |..........................................................       |  89%
  |                                                                       
  |...........................................................      |  90%
  |                                                                       
  |............................................................     |  92%
  |                                                                       
  |.............................................................    |  93%
  |                                                                       
  |..............................................................   |  95%
  |                                                                       
  |...............................................................  |  97%
  |                                                                       
  |................................................................ |  98%
  |                                                                       
  |.................................................................| 100%
output file: 10-import-data.R

unable to open connection to X11 display ''

processing file: 20-explore.Rmd

  |                                                                       
  |                                                                 |   0%
  |                                                                       
  |...                                                              |   5%
  |                                                                       
  |......                                                           |  10%
  |                                                                       
  |.........                                                        |  14%
  |                                                                       
  |............                                                     |  19%
  |                                                                       
  |...............                                                  |  24%
  |                                                                       
  |...................                                              |  29%
  |                                                                       
  |......................                                           |  33%
  |                                                                       
  |.........................                                        |  38%
  |                                                                       
  |............................                                     |  43%
  |                                                                       
  |...............................                                  |  48%
  |                                                                       
  |..................................                               |  52%
  |                                                                       
  |.....................................                            |  57%
  |                                                                       
  |........................................                         |  62%
  |                                                                       
  |...........................................                      |  67%
  |                                                                       
  |..............................................                   |  71%
  |                                                                       
  |..................................................               |  76%
  |                                                                       
  |.....................................................            |  81%
  |                                                                       
  |........................................................         |  86%
  |                                                                       
  |...........................................................      |  90%
  |                                                                       
  |..............................................................   |  95%
  |                                                                       
  |.................................................................| 100%
output file: 20-explore.R

unable to open connection to X11 display ''

processing file: 30-feature-engineering.Rmd

  |                                                                       
  |                                                                 |   0%
  |                                                                       
  |................................                                 |  50%
  |                                                                       
  |.................................................................| 100%
output file: 30-feature-engineering.R

unable to open connection to X11 display ''

processing file: 40-feature-selection.Rmd

  |                                                                       
  |                                                                 |   0%
  |                                                                       
  |....                                                             |   7%
  |                                                                       
  |.........                                                        |  13%
  |                                                                       
  |.............                                                    |  20%
  |                                                                       
  |.................                                                |  27%
  |                                                                       
  |......................                                           |  33%
  |                                                                       
  |..........................                                       |  40%
  |                                                                       
  |..............................                                   |  47%
  |                                                                       
  |...................................                              |  53%
  |                                                                       
  |.......................................                          |  60%
  |                                                                       
  |...........................................                      |  67%
  |                                                                       
  |................................................                 |  73%
  |                                                                       
  |....................................................             |  80%
  |                                                                       
  |........................................................         |  87%
  |                                                                       
  |.............................................................    |  93%
  |                                                                       
  |.................................................................| 100%
output file: 40-feature-selection.R

unable to open connection to X11 display ''

processing file: 50-modeling.Rmd

  |                                                                       
  |                                                                 |   0%
  |                                                                       
  |..                                                               |   3%
  |                                                                       
  |....                                                             |   6%
  |                                                                       
  |......                                                           |   9%
  |                                                                       
  |........                                                         |  12%
  |                                                                       
  |..........                                                       |  15%
  |                                                                       
  |............                                                     |  18%
  |                                                                       
  |..............                                                   |  21%
  |                                                                       
  |................                                                 |  24%
  |                                                                       
  |..................                                               |  27%
  |                                                                       
  |....................                                             |  30%
  |                                                                       
  |......................                                           |  33%
  |                                                                       
  |........................                                         |  36%
  |                                                                       
  |..........................                                       |  39%
  |                                                                       
  |............................                                     |  42%
  |                                                                       
  |..............................                                   |  45%
  |                                                                       
  |................................                                 |  48%
  |                                                                       
  |.................................                                |  52%
  |                                                                       
  |...................................                              |  55%
  |                                                                       
  |.....................................                            |  58%
  |                                                                       
  |.......................................                          |  61%
  |                                                                       
  |.........................................                        |  64%
  |                                                                       
  |...........................................                      |  67%
  |                                                                       
  |.............................................                    |  70%
  |                                                                       
  |...............................................                  |  73%
  |                                                                       
  |.................................................                |  76%
  |                                                                       
  |...................................................              |  79%
  |                                                                       
  |.....................................................            |  82%
  |                                                                       
  |.......................................................          |  85%
  |                                                                       
  |.........................................................        |  88%
  |                                                                       
  |...........................................................      |  91%
  |                                                                       
  |.............................................................    |  94%
  |                                                                       
  |...............................................................  |  97%
  |                                                                       
  |.................................................................| 100%
output file: 50-modeling.R

unable to open connection to X11 display ''

processing file: 60-results.Rmd

  |                                                                       
  |                                                                 |   0%
  |                                                                       
  |.....                                                            |   8%
  |                                                                       
  |..........                                                       |  15%
  |                                                                       
  |...............                                                  |  23%
  |                                                                       
  |....................                                             |  31%
  |                                                                       
  |.........................                                        |  38%
  |                                                                       
  |..............................                                   |  46%
  |                                                                       
  |...................................                              |  54%
  |                                                                       
  |........................................                         |  62%
  |                                                                       
  |.............................................                    |  69%
  |                                                                       
  |..................................................               |  77%
  |                                                                       
  |.......................................................          |  85%
  |                                                                       
  |............................................................     |  92%
  |                                                                       
  |.................................................................| 100%
output file: 60-results.R

Loading required package: pacman
the condition has length > 1 and only the first element will be usedthe condition has length > 1 and only the first element will be usedthe condition has length > 1 and only the first element will be usedInstalling packages into ‘/home/yaqooba/R/x86_64-pc-linux-gnu-library/3.6’
(as ‘lib’ is unspecified)
package ‘c’ is not available (for R version 3.6.0)trying URL 'https://cran.rstudio.com/src/contrib/plotly_4.9.2.1.tar.gz'
Content type 'application/x-gzip' length 3709741 bytes (3.5 MB)
==================================================
downloaded 3.5 MB

trying URL 'https://cran.rstudio.com/src/contrib/dotwhisker_0.5.0.tar.gz'
Content type 'application/x-gzip' length 935078 bytes (913 KB)
==================================================
downloaded 913 KB

trying URL 'https://cran.rstudio.com/src/contrib/broom_0.7.0.tar.gz'
Content type 'application/x-gzip' length 604195 bytes (590 KB)
==================================================
downloaded 590 KB

* installing *source* package ‘plotly’ ...
** package ‘plotly’ successfully unpacked and MD5 sums checked
** using staged installation
** R
** data
*** moving datasets to lazyload DB
** demo
** inst
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
*** copying figures
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (plotly)
* installing *source* package ‘broom’ ...
** package ‘broom’ successfully unpacked and MD5 sums checked
** using staged installation
** R
** inst
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
*** copying figures
** building package indices
** installing vignettes
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (broom)
* installing *source* package ‘dotwhisker’ ...
** package ‘dotwhisker’ successfully unpacked and MD5 sums checked
** using staged installation
** R
** inst
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** installing vignettes
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (dotwhisker)

The downloaded source packages are in
    ‘/tmp/Rtmp7eQEhF/downloaded_packages’
'BiocManager' not available.  Could not check Bioconductor.

Please use `install.packages('BiocManager')` and then retry.Installing package into ‘/home/yaqooba/R/x86_64-pc-linux-gnu-library/3.6’
(as ‘lib’ is unspecified)
trying URL 'https://cran.rstudio.com/src/contrib/plotly_4.9.2.1.tar.gz'
Content type 'application/x-gzip' length 3709741 bytes (3.5 MB)
==================================================
downloaded 3.5 MB

* installing *source* package ‘plotly’ ...
** package ‘plotly’ successfully unpacked and MD5 sums checked
** using staged installation
** R
** data
*** moving datasets to lazyload DB
** demo
** inst
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
*** copying figures
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (plotly)

The downloaded source packages are in
    ‘/tmp/Rtmp7eQEhF/downloaded_packages’

plotly installed

Purpose. In this work, we will explore the relation between identified measures of despair of interest (e.g., personality measures of self-consciousness, individual and composite item scores from the CES-D assessment) and descriptors of diseases of despair. We will achieve this goal through modeling the outcomes based on the included predictors, and robustly assess the importance of the included features in predicting the outcomes via bootstrapping. We will use two well-known machine learning models, random forests and LASSO, which are both frequently used to measure the relative importance of the predictors included in the models. Lastly, we’ll generate trained and tuned models using this reduced feature set which can be used by others wish to predict the identified outcomes.

Subject inclusion. For this investigation, we will omit the entirety of Wave 2. This is commonly done in analyses of AddHealth data due the design of the original study. Otherwise, our dataset will include only subjects who have predictor and outcome data in all of the waves.

Outcome variables. In this experiment, we assess prescription drug use at Wave 5.

Predictor variables. The predictors for these models are hand-picked, and based on previous work, relevance, and subject matter expertise. The set of predictors and the set of outcomes are disjoint. Predictors from Waves 1-4 (excluding Wave 2, see above) are included, and will be detailed in the following analysis.

seed= 3895
set.seed(seed)

Pipeline overview

Dataset generation

The predictors we will be using will be the the variable predictor_list loaded from 10-import-data.Rmd file. These initial set of predictors will be based of the list of variables that describe: 1. anxiety 2. depression 3. optimism

full_dataset %>% 
  group_by(p_drug) %>% 
  summarise(total = n(), type = class(p_drug))
`summarise()` ungrouping output (override with `.groups` argument)
## get the aids that you want
inner_aids <- get_inner(list(wave_data[[1]], wave_data[[3]], wave_data[[4]], wave_data[[5]]))

## get na_levels : dataset to recode all skip levels in variables
na_levels <- read_csv("na_levels.csv")

## use the features and ids that you want to select out what you want
pr_drug_ds <- full_dataset %>%
  add_demographics() %>%
  add_bio_despair() %>% 
  dplyr::select(aid, predictor_list, demographic_age_list, demographic_list, outcome) %>%
  recode_missing_levels(na_levels) %>% 
  filter(aid %in% inner_aids) %>%
  remove_subjects_not_in_wave1() %>%
  fix_outcome_variable("p_drug") %>% 
  mutate(p_drug = as.factor(p_drug)) %>% 
  select(-c(h5waist,h5bmi,h5dbp,h5bpjcls,h5bpcls4,h5sbp))
[1] "Recoding Missing Factor Variables"
[1] "Factor variables being recoded :  65"
[1] "Recoding Missing Numeric Variables"
[1] "Numeric variables being recoded :  10"
# full_dataset %>%
# add_pres_drug %>%
# add_demographics() %>%
# dplyr::select(aid, predictor_list, demographic_age_list, demographic_list, outcome) %>%
# filter(aid %in% inner_aids) %>%
# remove_subjects_not_in_wave1(filebase='Z:') %>%
# mutate_at(vars(-starts_with("age_")), as.factor) %>%
# mutate_at(vars(c(-starts_with("age_"),outcome)), fct_explicit_na) %>% 
# drop_na(outcome)


# Report about the characteristics of the subjects left out of the join
count_not_joined(wave_data = wave_data, number_waves_joined = 5)


# Validate the generated dataset using asserts
pr_drug_ds %>% 
  group_by(p_drug) %>% 
  summarise(total = n(), type = class(p_drug))
`summarise()` ungrouping output (override with `.groups` argument)

Data exploration and visualization

Here, we comment about the general characteristics of the data based on the provided visualizations. We comment on missingness of data, any strange or unusual behavior (e.g., strong imbalances), and any correlation that sticks out.

# Visualize distributions of variables of interest
pr_drug_ds %>% 
  dplyr::select(-aid) %>%
  graph_bar_discrete(df = .,
                     plot_title = "Distributions of Discrete Variables",
                     max_categories = 50,
                     num_rows = 3,
                     num_cols = 3,
                     x_axis_size = 12,
                     y_axis_size = 12,
                     title_size = 15)


# Visualize missingness
graph_missing(pr_drug_ds, 
              only_missing = TRUE,
              title = "Percent Missing",
              box_line_size = .5,
              label_size = .5,
              x_axis_size = 12,
              y_axis_size = 12,
              title_size = 15)


# Visualize correlation among first 20 predictors
pr_drug_ds %>%
  dplyr::select(1:20) %>%
  pairwise_cramers_v() %>%
  plot_cramer_v(x_axis_angle = 90,
                plot_title = "Association among Categorical Variables",
                interactive = TRUE)
full_dataset %>% 
  filter(aid %in% inner_aids) %>%
  add_wave_4_lipids() %>% 
  group_by(hdl) %>% 
  summarise(total = n(), type = class(hdl))
`summarise()` ungrouping output (override with `.groups` argument)
pr_drug_ds %>% 
  group_by(hdl) %>% 
  summarise(total = n(), type = class(hdl))
`summarise()` ungrouping output (override with `.groups` argument)

Machine learning split of the data

In this section, we split the data to ensure that our model is able to generalize to other datasets.

## split the data into relevant proportions desired
data_splits <- pr_drug_ds %>%
  split_data(strat_var = outcome, ratios=c(0.7, 0.2, 0.1))

# assemble list
training_df <- data_splits$train
validation_df <- data_splits$valid
testing_df <- data_splits$test

Robust feature evaluation

  • Here, we will justify the feature selection based on performance metrics; we assert that because of good model performance, we can expect that the features selected are reasonable because the models fit the data well (without being overtrained)
  • Here, the subject matter experts will comment on the selected features and their general applicability to the outcomes of interest.
  • Lastly, we will comment on differences in results between RF and LASSO

RF model

The RF models are chosen based on a grid search using the following the parameters:

  • max depth: maximum depth allowed for a single tree in the RF
  • number of trees: maximum number of trees allowed in the RF
  • mtries: the number of columns sampled for each tree split
  • min_rows: the minimum number of rows required to split the internal node
  • balance classes: whether to balance the classes or not
  • stopping_metric: metric which results in early stopping of training of the model
  • categorical encoding: use one hot encoding to create straightforward comparison with LASSO

The following table displays the mean performance metrics for the bootstrapped models on the validation set, removing values for which there are NA.

mean_bs_rf_perf <- get_metric_set_from_perfs(pr_drug_rf$perfs) %>%
  dplyr::select(accuracy, mpce, sens, spec, ppv, npv, roc_auc, pr_auc,
                tns, tps, fns, fps, no_n, no_p,  err_rate, bal_accuracy, everything()) %>%
  summarise_if(is.numeric, mean, na.rm=TRUE) %>%
  mutate(model = 'bs_rf') %>%
  dplyr::select(model, everything())

mean_bs_rf_perf

As shown, the bootstrapped models tend to have high specificity but low sensitivity, indicating that there is a challenge in identifying subjects with suicidal ideation.

Feature importances: Random Forest

Mean decrease in impurity (MDI)

boot_rf_mdi <- pr_drug_rf$mdi %>%
  get_median_placement(use_base_var = TRUE) %>%
  add_attribute_names('predictor', full_dataset) %>%
  dplyr::select(predictor, att_name, overall_rank)
`summarise()` ungrouping output (override with `.groups` argument)
head(boot_rf_mdi, 20)

This table returns the MDI variable importance ranks that returned from each of the bootstrapped models.

# Needs to be fixed so that axes don't overlap each other and obscure understanding
plot_placement_boxplot(pr_drug_rf$mdi)

Permutation importance

Now, let’s look at the permutation importance:

boot_rf_perm_plt <- pr_drug_rf$models %>%
  get_aggregated_permute_imp(training_df, outcome=outcome)
met <- 'pr_auc'
boot_rf_perm <- boot_rf_perm_plt %>%
  get_permute_placement(metric_oi=met) %>%
  add_attribute_names('predictor', full_dataset) %>%
  dplyr::select(predictor, everything())

head(boot_rf_perm, 20)

MDI vs Permutation importance

In this step, we assess the differences generated between the different types of importances.

cbind(boot_rf_mdi[1:20,], dplyr::select(boot_rf_perm[1:20,], -all_of(met)))

As shown, the MDI importance suffers from imbalances due to the number of values associated with a predictor. Because the wave ages have so many more values than the other factors, this artificially inflates their importance in MDI. The permutation importance is more intuitive.

plot_permute_var_imp(boot_rf_perm, metric = pr_auc)

LASSO model

In this step, we model the relation between the outcomes and the predictors using a linear regression with L2 regularization. This drives the importance of unimportant and redudant features towards zero.

# Function parameters
lasso_params <- list(alpha = c(1))
# Call modeling function using function parameters and show visualization of results.  Recommend the number of features that should be used.  Report performance metric stats.

pr_drug_lasso <- model_feature_selection( "Lasso",
                                          training_frame = training_df,
                                          validation_frame = validation_df,
                                          hyper_params = lasso_params,
                                          outcome = outcome, 
                                          n = n_boot)
mean_bs_lasso_perf <- get_metric_set_from_perfs(pr_drug_lasso$perfs) %>%
  dplyr::select(accuracy, mpce, sens, spec, ppv, npv, roc_auc, pr_auc,
                tns, tps, fns, fps, no_n, no_p,  err_rate, bal_accuracy, everything()) %>%
  summarise_if(is.numeric, mean, na.rm=TRUE) %>% 
  mutate(model='bs_lasso') %>%
  dplyr::select(model, everything())

mean_bs_lasso_perf

Feature importances: LASSO

Coefficient-based variable importance

boot_lasso_mdi <- pr_drug_lasso$mdi %>%
  get_median_placement(use_base_var = TRUE) %>%
  add_attribute_names('predictor', full_dataset) %>%
  dplyr::select(predictor, att_name, overall_rank)
`summarise()` ungrouping output (override with `.groups` argument)
head(boot_lasso_mdi, 20)
plot_placement_boxplot(pr_drug_lasso$mdi)

Permutation importance

boot_lasso_perm_plt <- pr_drug_lasso$models %>%
  get_aggregated_permute_imp(training_df, outcome=outcome)
boot_lasso_perm <- boot_lasso_perm_plt %>%
  get_permute_placement(metric_oi=met) %>% #set in random forest section
  add_attribute_names('predictor', full_dataset) %>%
  dplyr::select(predictor, everything())

head(boot_lasso_perm, 20)
plot_permute_var_imp(boot_lasso_perm, metric = pr_auc)

Coefficient vs. Permutation importance

Now, we compare the feature importances generated by the two different approaches. The traditional method of evaluating feature importance for regression methods is through analysis of the coefficients.

cbind(boot_lasso_mdi[1:20,], dplyr::select(boot_lasso_perm[1:20,], -met))
Note: Using an external vector in selections is ambiguous.
ℹ Use `all_of(met)` instead of `met` to silence this message.
ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.

Comparison: Model Type Mean Performance

The following table compares the mean performance of bootstrapped random forests to the mean performance of bootstrapped LASSO methods.

bs_comp_perfs <- rbind(mean_bs_rf_perf, mean_bs_lasso_perf) 
bs_comp_perfs

Comparison: Model Type Feature Importance

Here, we look at the aggregated results of the bootstrapped predictors and compare the models generated to each other.

joined_results <- boot_rf_perm %>%
  dplyr::select(-met) %>%
  full_join(dplyr::select(boot_lasso_perm, -met), by=c("predictor", "att_name"), suffix=c('.rf', '.lasso')) %>%
  mutate(mean_rank = (overall_rank.rf+overall_rank.lasso)/2) %>%
  arrange(mean_rank)

head(joined_results, 20)

The following visualization provides the intuition about the differences in the rankings between model types. They’re ordered by the overall mean importance, and for a given variable, the differences in rank are shown.

# Comparison of top_n features
joined_results %>%
  compare_feature_select(interactive = TRUE,
                         top_n = 100,
                         opacity = 0.50,
                         plot_title = "Permutation Importance of Predictors by Model")
Note: Using an external vector in selections is ambiguous.
ℹ Use `all_of(sel_cols)` instead of `sel_cols` to silence this message.
ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.
`group_by_()` is deprecated as of dplyr 0.7.0.
Please use `group_by()` instead.
See vignette('programming') for more help
This warning is displayed once every 8 hours.
Call `lifecycle::last_warnings()` to see where this warning was generated.

Generation of final model

RF model

In this step, we build the final model for the random forest. We use slightly more values in order to come up with the best model, keeping in mind the number of combinations that are required to run to evaluate the grid.


# # Spans of hyper parameters for random forest
rf_params <- list(max_depth = 50,
                  ntrees = 150,
                  mtries = seq(-1, 30, by=5),
                  min_rows = seq(5, 60, by=5),
                  balance_classes = c(TRUE, FALSE),
                  stopping_metric = 'AUCPR',
                  categorical_encoding = 'one_hot_explicit')

# rf_params <- list(max_depth = seq(20, 50, 20),
#                   balance_classes = TRUE,
#                   categorical_encoding= 'one_hot_explicit')

# Function parameters
final_model_rf <- rf_model(outcome,
                           training_frame = training_df,
                           validation_frame = validation_df,
                           nfolds = 5,
                           hyper_params = rf_params, model_seed=seed)
`aid` is in your training data frame.  Dropping for modeling purposes.
`aid` is in your validation data frame.  Dropping for modeling purposes.
`summarise()` ungrouping output (override with `.groups` argument)

Performance

The final random forest performance metrics are shown below:

# show model final performance
print(final_model_rf[[2]])

Features: permutation importance

final_rf_perm_plt <- c(final_model_rf[[1]]) %>%
  get_aggregated_permute_imp(training_df, outcome=outcome)
final_rf_perm <- final_rf_perm_plt %>%
  get_permute_placement(metric_oi=met) %>%
  add_attribute_names('predictor', full_dataset) %>%
  dplyr::select(predictor, everything())

head(final_rf_perm, 20)
plot_permute_var_imp(final_rf_perm, metric = pr_auc)

Comparison with bootstrap results

This section investigates the differences in the bootstrap results vs the features generated from the random forest final model. The following table shows the overall differences in rank.

rf_joined_results <- final_rf_perm %>%
  dplyr::select(-met) %>%
  full_join(dplyr::select(boot_rf_perm, -met), by=c("predictor", "att_name"), suffix=c('.final', '.bootstrap')) %>%
  mutate(mean_rank = (overall_rank.final + overall_rank.bootstrap)/2) %>%
  arrange(mean_rank)

head(rf_joined_results, 20)

The following plot provides visualizations for the difference in the final model rankings vs the bootstrap.

# Comparison of top_n features
rf_joined_results %>%
  compare_feature_select(sel_cols = c("overall_rank.final", "overall_rank.bootstrap"),
                         interactive = TRUE,
                         top_n = 100,
                         opacity = 0.50,
                         plot_title = "Permutation Importance of Predictors: Final vs. Bootstrap")

LASSO model

Now, we create the final model for LASSO. There is no substantial difference between this method and the bootstrap methods, other than the data upon which the model is being built.

# Function parameters
lasso_params <- list(alpha = c(1))

final_model_lasso <- lasso_model(training_frame = training_df,
                                 validation_frame = validation_df,
                                 outcome = outcome,
                                 nfolds = 5,
                                 hyper_params = lasso_params)

The final LASSO performance metrics are shown below:

# show model final performance
print(final_model_lasso[[2]])

Features: permutation importance

final_lasso_perm_plt <- c(final_model_lasso[[1]]) %>%
  get_aggregated_permute_imp(training_df, outcome=outcome)
final_lasso_perm <- final_lasso_perm_plt %>%
  get_permute_placement(metric_oi=met) %>%
  add_attribute_names('predictor', full_dataset) %>%
  dplyr::select(predictor, everything())

head(final_lasso_perm, 20)
plot_permute_var_imp(final_lasso_perm, metric = pr_auc)

Comparison with bootstrap results

This section investigates the differences in the bootstrap results vs the features generated from the LASSO final model. The following table shows the overall differences in rank.

lasso_joined_results <- final_lasso_perm %>%
  dplyr::select(-met) %>%
  full_join(dplyr::select(boot_lasso_perm, -met), by=c("predictor", "att_name"), suffix=c('.final', '.bootstrap')) %>%
  mutate(mean_rank = (overall_rank.final + overall_rank.bootstrap)/2) %>%
  arrange(mean_rank)

head(lasso_joined_results, 20)

The following plot provides visualizations for the difference in the final model rankings vs the bootstrap.

# Comparison of top_n features
lasso_joined_results %>%
  compare_feature_select(sel_cols = c("overall_rank.final", "overall_rank.bootstrap"),
                         interactive = TRUE,
                         top_n = 100,
                         opacity = 0.50,
                         plot_title = "Permutation Importance of Predictors: Final vs. Bootstrap")

Comparison: Final model features

Here, we compare the features generated by the permutation importance between the two final models.

rf_lasso_final_joined_results <- final_rf_perm %>%
  dplyr::select(-met) %>%
  full_join(dplyr::select(final_lasso_perm, -met), by=c("predictor", "att_name"), suffix=c('.rf', '.lasso')) %>%
  mutate(mean_rank = (overall_rank.rf+overall_rank.lasso)/2) %>%
  arrange(mean_rank)

head(rf_lasso_final_joined_results, 20)

The following visualization provides the intuition about the differences in the rankings between the final model types. They’re ordered by the overall mean importance, and for a given variable, the differences in rank are shown.

# Comparison of top_n features
rf_lasso_final_joined_results %>%
  compare_feature_select(sel_cols = c("overall_rank.rf", "overall_rank.lasso"),
                         interactive = TRUE,
                         top_n = 100,
                         opacity = 0.50,
                         plot_title = "Permutation Importance of Predictors: Random Forest vs Lasso")

Comparison: Final model performance

With the final models generated, we’re now able to compare their performance metrics.

# Comparison of performance metrics
valid_perf <- get_metric_set_from_perfs(perf_list = list(final_model_rf[[2]], final_model_lasso[[2]])) %>%
  mutate(model = c('rf', 'lasso'))

testing_perf <- get_metric_set_from_models(testing_df, list(final_model_rf[[1]], final_model_lasso[[1]]), out=outcome) %>%
  mutate(model = c('rf', 'lasso'))
`summarise()` ungrouping output (override with `.groups` argument)
`summarise()` ungrouping output (override with `.groups` argument)

Validation and selection. The following table shows the comparison between models in terms of the validation set. We can select our final model based on the best performing model according to the metric.

print(valid_perf)

Testing performance. The following shows the performance of both the models on the test set. Note that although we don’t use this test set to evaluate the final models, we can still see how our selected method would have performed.

print(testing_perf)

The following plots show a comparison between the performance of the models on the validation and test sets. Again, we don’t choose the model based on the test set, but curiosity dictates that we view this performance.

# Show plots side by side
metrics_of_interest = c('model', 'accuracy', 'bal_accuracy', 'mpce', 'sens', 'spec', 'ppv', 'npv', 'pr_auc', 'roc_auc')
valid_plt <- plot_metric_set(dplyr::select(valid_perf, all_of(metrics_of_interest)), plot_title = "Model comparison for validation set")
test_plt <- plot_metric_set(dplyr::select(testing_perf, all_of(metrics_of_interest)), plot_title = "Model comparison for testing set")
gridExtra::grid.arrange(gridExtra::arrangeGrob(valid_plt, test_plt, ncol=2, nrow=1))

Outcome variable discussion

Here, the subject matter experts will comment on the the differences in the features obtained between the studied outcomes variables and discuss the discrepancies and/or cohesion.

# Show differences in features obtained
LS0tCnRpdGxlOiAiNzMtZXhwZXJpbWVudHMtcHItZHJ1ZyIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRoZW1lOiBsdW1lbgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDQKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHllcwplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge3Igc291cmNlIGZpbGVzfQpzb3VyY2UoImZ1bmN0aW9uX2ltcG9ydC5SIikKYGBgCgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgoqKlB1cnBvc2UuKiogSW4gdGhpcyB3b3JrLCB3ZSB3aWxsIGV4cGxvcmUgdGhlIHJlbGF0aW9uIGJldHdlZW4gaWRlbnRpZmllZCBtZWFzdXJlcyBvZiBkZXNwYWlyIG9mIGludGVyZXN0IChlLmcuLCBwZXJzb25hbGl0eSBtZWFzdXJlcyBvZiBzZWxmLWNvbnNjaW91c25lc3MsIGluZGl2aWR1YWwgYW5kIGNvbXBvc2l0ZSBpdGVtIHNjb3JlcyBmcm9tIHRoZSBDRVMtRCBhc3Nlc3NtZW50KSBhbmQgZGVzY3JpcHRvcnMgb2YgZGlzZWFzZXMgb2YgZGVzcGFpci4gIFdlIHdpbGwgYWNoaWV2ZSB0aGlzIGdvYWwgdGhyb3VnaCBtb2RlbGluZyB0aGUgb3V0Y29tZXMgYmFzZWQgb24gdGhlIGluY2x1ZGVkIHByZWRpY3RvcnMsIGFuZCByb2J1c3RseSBhc3Nlc3MgdGhlIGltcG9ydGFuY2Ugb2YgdGhlIGluY2x1ZGVkIGZlYXR1cmVzIGluIHByZWRpY3RpbmcgdGhlIG91dGNvbWVzIHZpYSBib290c3RyYXBwaW5nLiAgV2Ugd2lsbCB1c2UgdHdvIHdlbGwta25vd24gbWFjaGluZSBsZWFybmluZyBtb2RlbHMsIHJhbmRvbSBmb3Jlc3RzIGFuZCBMQVNTTywgd2hpY2ggYXJlIGJvdGggZnJlcXVlbnRseSB1c2VkIHRvIG1lYXN1cmUgdGhlIHJlbGF0aXZlIGltcG9ydGFuY2Ugb2YgdGhlIHByZWRpY3RvcnMgaW5jbHVkZWQgaW4gdGhlIG1vZGVscy4gIExhc3RseSwgd2UnbGwgZ2VuZXJhdGUgdHJhaW5lZCBhbmQgdHVuZWQgbW9kZWxzIHVzaW5nIHRoaXMgcmVkdWNlZCBmZWF0dXJlIHNldCB3aGljaCBjYW4gYmUgdXNlZCBieSBvdGhlcnMgd2lzaCB0byBwcmVkaWN0IHRoZSBpZGVudGlmaWVkIG91dGNvbWVzLgoKKipTdWJqZWN0IGluY2x1c2lvbi4qKiBGb3IgdGhpcyBpbnZlc3RpZ2F0aW9uLCB3ZSB3aWxsIG9taXQgdGhlIGVudGlyZXR5IG9mIFdhdmUgMi4gIFRoaXMgaXMgY29tbW9ubHkgZG9uZSBpbiBhbmFseXNlcyBvZiBBZGRIZWFsdGggZGF0YSBkdWUgdGhlIGRlc2lnbiBvZiB0aGUgb3JpZ2luYWwgc3R1ZHkuICBPdGhlcndpc2UsIG91ciBkYXRhc2V0IHdpbGwgaW5jbHVkZSBvbmx5IHN1YmplY3RzIHdobyBoYXZlIHByZWRpY3RvciBhbmQgb3V0Y29tZSBkYXRhIGluIF9hbGxfIG9mIHRoZSB3YXZlcy4KCioqT3V0Y29tZSB2YXJpYWJsZXMuKiogSW4gdGhpcyBleHBlcmltZW50LCB3ZSBhc3Nlc3MgX3ByZXNjcmlwdGlvbiBkcnVnIHVzZV8gYXQgV2F2ZSA1LiAgCgoqKlByZWRpY3RvciB2YXJpYWJsZXMuKiogVGhlIHByZWRpY3RvcnMgZm9yIHRoZXNlIG1vZGVscyBhcmUgaGFuZC1waWNrZWQsIGFuZCBiYXNlZCBvbiBwcmV2aW91cyB3b3JrLCByZWxldmFuY2UsIGFuZCBzdWJqZWN0IG1hdHRlciBleHBlcnRpc2UuIFRoZSBzZXQgb2YgcHJlZGljdG9ycyBhbmQgdGhlIHNldCBvZiBvdXRjb21lcyBhcmUgZGlzam9pbnQuICBQcmVkaWN0b3JzIGZyb20gV2F2ZXMgMS00IChleGNsdWRpbmcgV2F2ZSAyLCBzZWUgYWJvdmUpIGFyZSBpbmNsdWRlZCwgYW5kIHdpbGwgYmUgZGV0YWlsZWQgaW4gdGhlIGZvbGxvd2luZyBhbmFseXNpcy4KCmBgYHtyIGxvYWQgbGlicmFyaWVzLCBpbmNsdWRlPUZBTFNFfQojIFVzZSBwYWNtYW4sIHdoaWNoIGZvcmNlcyBhbiBpbnN0YWxsIGlmIHRoZSBsaWJyYXJ5IGlzbid0IHByZXNlbnQgb24gdGhlIHJ1bm5pbmcgbWFjaGluZQppZiAoIXJlcXVpcmUoInBhY21hbiIpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQpwYWNtYW46OnBfaW5zdGFsbChwbG90bHkpCnBhY21hbjo6cF9sb2FkKHRpZHl2ZXJzZSwgamFuaXRvciwgcnNhbXBsZSwgaDJvLCBmdXJyciwgcmNvbXBhbmlvbiwgZ2djb3JycGxvdCkKCmBgYAoKYGBge3IgaW5pdGlhbGl6YXRpb25zLCBpbmNsdWRlPUZBTFNFfQpoMm8uaW5pdCgpIApoMm8ubm9fcHJvZ3Jlc3MoKQpmdXR1cmU6OnBsYW4obXVsdGlwcm9jZXNzKQpgYGAKCmBgYHtyIHNlZWRzIGZvciByZXByb2R1Y2liaWxpdHl9CnNlZWQ9IDM4OTUKc2V0LnNlZWQoc2VlZCkKYGBgCiMgUGlwZWxpbmUgb3ZlcnZpZXcKYGBge3IgZWNobz1GQUxTRSwgb3V0LndpZHRoPSc1MCUnfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygnLi9pbWcvcGlwZWxpbmVfb3ZlcnZpZXcuanBnJykKYGBgCiMgRGF0YXNldCBnZW5lcmF0aW9uCgpUaGUgcHJlZGljdG9ycyB3ZSB3aWxsIGJlIHVzaW5nIHdpbGwgYmUgdGhlIHRoZSB2YXJpYWJsZSBgcHJlZGljdG9yX2xpc3RgIGxvYWRlZCBmcm9tIGAxMC1pbXBvcnQtZGF0YS5SbWRgIGZpbGUuIFRoZXNlIGluaXRpYWwgc2V0IG9mIHByZWRpY3RvcnMgd2lsbCBiZSBiYXNlZCBvZiB0aGUgbGlzdCBvZiB2YXJpYWJsZXMgdGhhdCBkZXNjcmliZToKMS4gYW54aWV0eQoyLiBkZXByZXNzaW9uIAozLiBvcHRpbWlzbSAKCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQojIyBzZXQgb3V0Y29tZSB2YXJpYWJsZSBvZiBpbnRlcmVzdApvdXRjb21lID0gJ3BfZHJ1ZycgIyBUaGlzIGlzIHRoZSBiaW5hcnkgdmFyaWFibGUgcmVwcmVzZW50aW5nIHByZXNjcmlwdGlvbiBkcnVnIHVzZSBhdCB3YXZlIDUKCndhdmVfZGF0YSA8LSBsb2FkX3dhdmVzKDE6NSkKZnVsbF9kYXRhc2V0IDwtIGdldF93b3JraW5nX2RhdGFzZXRfZnVsbCh3YXZlX2RhdGEsIGpvaW5fdHlwZSA9ICdmdWxsJykgJT4lICBhZGRfcHJlc19kcnVnKCkgCmBgYAoKCgpgYGB7cn0KZnVsbF9kYXRhc2V0ICU+JSAKICBncm91cF9ieShwX2RydWcpICU+JSAKICBzdW1tYXJpc2UodG90YWwgPSBuKCksIHR5cGUgPSBjbGFzcyhwX2RydWcpKQpgYGAKCgpgYGB7ciwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQojIyBnZXQgdGhlIGFpZHMgdGhhdCB5b3Ugd2FudAppbm5lcl9haWRzIDwtIGdldF9pbm5lcihsaXN0KHdhdmVfZGF0YVtbMV1dLCB3YXZlX2RhdGFbWzNdXSwgd2F2ZV9kYXRhW1s0XV0sIHdhdmVfZGF0YVtbNV1dKSkKCiMjIGdldCBuYV9sZXZlbHMgOiBkYXRhc2V0IHRvIHJlY29kZSBhbGwgc2tpcCBsZXZlbHMgaW4gdmFyaWFibGVzCm5hX2xldmVscyA8LSByZWFkX2NzdigibmFfbGV2ZWxzLmNzdiIpCgojIyB1c2UgdGhlIGZlYXR1cmVzIGFuZCBpZHMgdGhhdCB5b3Ugd2FudCB0byBzZWxlY3Qgb3V0IHdoYXQgeW91IHdhbnQKcHJfZHJ1Z19kcyA8LSBmdWxsX2RhdGFzZXQgJT4lCiAgYWRkX2RlbW9ncmFwaGljcygpICU+JQogIGFkZF9iaW9fZGVzcGFpcigpICU+JSAKICBkcGx5cjo6c2VsZWN0KGFpZCwgcHJlZGljdG9yX2xpc3QsIGRlbW9ncmFwaGljX2FnZV9saXN0LCBkZW1vZ3JhcGhpY19saXN0LCBvdXRjb21lKSAlPiUKICByZWNvZGVfbWlzc2luZ19sZXZlbHMobmFfbGV2ZWxzKSAlPiUgCiAgZmlsdGVyKGFpZCAlaW4lIGlubmVyX2FpZHMpICU+JQogIHJlbW92ZV9zdWJqZWN0c19ub3RfaW5fd2F2ZTEoKSAlPiUKICBmaXhfb3V0Y29tZV92YXJpYWJsZSgicF9kcnVnIikgJT4lIAogIG11dGF0ZShwX2RydWcgPSBhcy5mYWN0b3IocF9kcnVnKSkgJT4lIAogIHNlbGVjdCgtYyhoNXdhaXN0LGg1Ym1pLGg1ZGJwLGg1YnBqY2xzLGg1YnBjbHM0LGg1c2JwKSkKCgojIGZ1bGxfZGF0YXNldCAlPiUKIyBhZGRfcHJlc19kcnVnICU+JQojIGFkZF9kZW1vZ3JhcGhpY3MoKSAlPiUKIyBkcGx5cjo6c2VsZWN0KGFpZCwgcHJlZGljdG9yX2xpc3QsIGRlbW9ncmFwaGljX2FnZV9saXN0LCBkZW1vZ3JhcGhpY19saXN0LCBvdXRjb21lKSAlPiUKIyBmaWx0ZXIoYWlkICVpbiUgaW5uZXJfYWlkcykgJT4lCiMgcmVtb3ZlX3N1YmplY3RzX25vdF9pbl93YXZlMShmaWxlYmFzZT0nWjonKSAlPiUKIyBtdXRhdGVfYXQodmFycygtc3RhcnRzX3dpdGgoImFnZV8iKSksIGFzLmZhY3RvcikgJT4lCiMgbXV0YXRlX2F0KHZhcnMoYygtc3RhcnRzX3dpdGgoImFnZV8iKSxvdXRjb21lKSksIGZjdF9leHBsaWNpdF9uYSkgJT4lIAojIGRyb3BfbmEob3V0Y29tZSkKCgojIFJlcG9ydCBhYm91dCB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSBzdWJqZWN0cyBsZWZ0IG91dCBvZiB0aGUgam9pbgpjb3VudF9ub3Rfam9pbmVkKHdhdmVfZGF0YSA9IHdhdmVfZGF0YSwgbnVtYmVyX3dhdmVzX2pvaW5lZCA9IDUpCgojIFZhbGlkYXRlIHRoZSBnZW5lcmF0ZWQgZGF0YXNldCB1c2luZyBhc3NlcnRzCgpgYGAKCmBgYHtyfQpwcl9kcnVnX2RzICU+JSAKICBncm91cF9ieShwX2RydWcpICU+JSAKICBzdW1tYXJpc2UodG90YWwgPSBuKCksIHR5cGUgPSBjbGFzcyhwX2RydWcpKQpgYGAKCiMgRGF0YSBleHBsb3JhdGlvbiBhbmQgdmlzdWFsaXphdGlvbgpIZXJlLCB3ZSBjb21tZW50IGFib3V0IHRoZSBnZW5lcmFsIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgZGF0YSBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgdmlzdWFsaXphdGlvbnMuICBXZSBjb21tZW50IG9uIG1pc3NpbmduZXNzIG9mIGRhdGEsIGFueSBzdHJhbmdlIG9yIHVudXN1YWwgYmVoYXZpb3IgKGUuZy4sIHN0cm9uZyBpbWJhbGFuY2VzKSwgYW5kIGFueSBjb3JyZWxhdGlvbiB0aGF0IHN0aWNrcyBvdXQuCmBgYHtyIGVkYX0KIyBWaXN1YWxpemUgZGlzdHJpYnV0aW9ucyBvZiB2YXJpYWJsZXMgb2YgaW50ZXJlc3QKcHJfZHJ1Z19kcyAlPiUgCiAgZHBseXI6OnNlbGVjdCgtYWlkKSAlPiUKICBncmFwaF9iYXJfZGlzY3JldGUoZGYgPSAuLAogICAgICAgICAgICAgICAgICAgICBwbG90X3RpdGxlID0gIkRpc3RyaWJ1dGlvbnMgb2YgRGlzY3JldGUgVmFyaWFibGVzIiwKICAgICAgICAgICAgICAgICAgICAgbWF4X2NhdGVnb3JpZXMgPSA1MCwKICAgICAgICAgICAgICAgICAgICAgbnVtX3Jvd3MgPSAzLAogICAgICAgICAgICAgICAgICAgICBudW1fY29scyA9IDMsCiAgICAgICAgICAgICAgICAgICAgIHhfYXhpc19zaXplID0gMTIsCiAgICAgICAgICAgICAgICAgICAgIHlfYXhpc19zaXplID0gMTIsCiAgICAgICAgICAgICAgICAgICAgIHRpdGxlX3NpemUgPSAxNSkKCiMgVmlzdWFsaXplIG1pc3NpbmduZXNzCmdyYXBoX21pc3NpbmcocHJfZHJ1Z19kcywgCiAgICAgICAgICAgICAgb25seV9taXNzaW5nID0gVFJVRSwKICAgICAgICAgICAgICB0aXRsZSA9ICJQZXJjZW50IE1pc3NpbmciLAogICAgICAgICAgICAgIGJveF9saW5lX3NpemUgPSAuNSwKICAgICAgICAgICAgICBsYWJlbF9zaXplID0gLjUsCiAgICAgICAgICAgICAgeF9heGlzX3NpemUgPSAxMiwKICAgICAgICAgICAgICB5X2F4aXNfc2l6ZSA9IDEyLAogICAgICAgICAgICAgIHRpdGxlX3NpemUgPSAxNSkKCiMgVmlzdWFsaXplIGNvcnJlbGF0aW9uIGFtb25nIGZpcnN0IDIwIHByZWRpY3RvcnMKcHJfZHJ1Z19kcyAlPiUKICBkcGx5cjo6c2VsZWN0KDE6MjApICU+JQogIHBhaXJ3aXNlX2NyYW1lcnNfdigpICU+JQogIHBsb3RfY3JhbWVyX3YoeF9heGlzX2FuZ2xlID0gOTAsCiAgICAgICAgICAgICAgICBwbG90X3RpdGxlID0gIkFzc29jaWF0aW9uIGFtb25nIENhdGVnb3JpY2FsIFZhcmlhYmxlcyIsCiAgICAgICAgICAgICAgICBpbnRlcmFjdGl2ZSA9IFRSVUUpCmBgYAoKYGBge3J9CmZ1bGxfZGF0YXNldCAlPiUgCiAgZmlsdGVyKGFpZCAlaW4lIGlubmVyX2FpZHMpICU+JQogIGFkZF93YXZlXzRfbGlwaWRzKCkgJT4lIAogIGdyb3VwX2J5KGhkbCkgJT4lIAogIHN1bW1hcmlzZSh0b3RhbCA9IG4oKSwgdHlwZSA9IGNsYXNzKGhkbCkpCmBgYAoKCmBgYHtyfQpwcl9kcnVnX2RzICU+JSAKICBncm91cF9ieShoZGwpICU+JSAKICBzdW1tYXJpc2UodG90YWwgPSBuKCksIHR5cGUgPSBjbGFzcyhoZGwpKQoKYGBgCgoKCgojIE1hY2hpbmUgbGVhcm5pbmcgc3BsaXQgb2YgdGhlIGRhdGEKCkluIHRoaXMgc2VjdGlvbiwgd2Ugc3BsaXQgdGhlIGRhdGEgdG8gZW5zdXJlIHRoYXQgb3VyIG1vZGVsIGlzIGFibGUgdG8gZ2VuZXJhbGl6ZSB0byBvdGhlciBkYXRhc2V0cy4KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMjIHNwbGl0IHRoZSBkYXRhIGludG8gcmVsZXZhbnQgcHJvcG9ydGlvbnMgZGVzaXJlZApkYXRhX3NwbGl0cyA8LSBwcl9kcnVnX2RzICU+JQogIHNwbGl0X2RhdGEoc3RyYXRfdmFyID0gb3V0Y29tZSwgcmF0aW9zPWMoMC43LCAwLjIsIDAuMSkpCgojIGFzc2VtYmxlIGxpc3QKdHJhaW5pbmdfZGYgPC0gZGF0YV9zcGxpdHMkdHJhaW4KdmFsaWRhdGlvbl9kZiA8LSBkYXRhX3NwbGl0cyR2YWxpZAp0ZXN0aW5nX2RmIDwtIGRhdGFfc3BsaXRzJHRlc3QKYGBgCgojIFJvYnVzdCBmZWF0dXJlIGV2YWx1YXRpb24gey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9Ci0gSGVyZSwgd2Ugd2lsbCBqdXN0aWZ5IHRoZSBmZWF0dXJlIHNlbGVjdGlvbiBiYXNlZCBvbiBwZXJmb3JtYW5jZSBtZXRyaWNzOyB3ZSBhc3NlcnQgdGhhdCBiZWNhdXNlIG9mIGdvb2QgbW9kZWwgcGVyZm9ybWFuY2UsIHdlIGNhbiBleHBlY3QgdGhhdCB0aGUgZmVhdHVyZXMgc2VsZWN0ZWQgYXJlIHJlYXNvbmFibGUgYmVjYXVzZSB0aGUgbW9kZWxzIGZpdCB0aGUgZGF0YSB3ZWxsICh3aXRob3V0IGJlaW5nIG92ZXJ0cmFpbmVkKQotIEhlcmUsIHRoZSBzdWJqZWN0IG1hdHRlciBleHBlcnRzIHdpbGwgY29tbWVudCBvbiB0aGUgc2VsZWN0ZWQgZmVhdHVyZXMgYW5kIHRoZWlyIGdlbmVyYWwgYXBwbGljYWJpbGl0eSB0byB0aGUgb3V0Y29tZXMgb2YgaW50ZXJlc3QuCi0gTGFzdGx5LCB3ZSB3aWxsIGNvbW1lbnQgb24gZGlmZmVyZW5jZXMgaW4gcmVzdWx0cyBiZXR3ZWVuIFJGIGFuZCBMQVNTTwoKIyMgUkYgbW9kZWwKVGhlIFJGIG1vZGVscyBhcmUgY2hvc2VuIGJhc2VkIG9uIGEgZ3JpZCBzZWFyY2ggdXNpbmcgdGhlIGZvbGxvd2luZyB0aGUgcGFyYW1ldGVyczogCgotIG1heCBkZXB0aDogbWF4aW11bSBkZXB0aCBhbGxvd2VkIGZvciBhIHNpbmdsZSB0cmVlIGluIHRoZSBSRiAgCi0gbnVtYmVyIG9mIHRyZWVzOiBtYXhpbXVtIG51bWJlciBvZiB0cmVlcyBhbGxvd2VkIGluIHRoZSBSRiAgCi0gbXRyaWVzOiB0aGUgbnVtYmVyIG9mIGNvbHVtbnMgc2FtcGxlZCBmb3IgZWFjaCB0cmVlIHNwbGl0ICAKLSBtaW5fcm93czogdGhlIG1pbmltdW0gbnVtYmVyIG9mIHJvd3MgcmVxdWlyZWQgdG8gc3BsaXQgdGhlIGludGVybmFsIG5vZGUKLSBiYWxhbmNlIGNsYXNzZXM6IHdoZXRoZXIgdG8gYmFsYW5jZSB0aGUgY2xhc3NlcyBvciBub3QKLSBzdG9wcGluZ19tZXRyaWM6IG1ldHJpYyB3aGljaCByZXN1bHRzIGluIGVhcmx5IHN0b3BwaW5nIG9mIHRyYWluaW5nIG9mIHRoZSBtb2RlbAotIGNhdGVnb3JpY2FsIGVuY29kaW5nOiB1c2Ugb25lIGhvdCBlbmNvZGluZyB0byBjcmVhdGUgc3RyYWlnaHRmb3J3YXJkIGNvbXBhcmlzb24gd2l0aCBMQVNTTwoKYGBge3IgZmVhdHVyZSBzZWxlY3Rpb24gcmYsIGluY2x1ZGU9RkFMU0V9CgojIFNwYW5zIG9mIGh5cGVyIHBhcmFtZXRlcnMgZm9yIHJhbmRvbSBmb3Jlc3QKcmZfcGFyYW1zIDwtIGxpc3QobWF4X2RlcHRoID0gNTAsCiAgICAgICAgICAgICAgICAgIG50cmVlcyA9IDE1MCwKICAgICAgICAgICAgICAgICAgbXRyaWVzID0gc2VxKC0xLCAyMCwgYnk9MTApLAogICAgICAgICAgICAgICAgICBtaW5fcm93cyA9IHNlcSgyMCwgNjAsIGJ5PTIwKSwKICAgICAgICAgICAgICAgICAgYmFsYW5jZV9jbGFzc2VzID0gYyhUUlVFLCBGQUxTRSksCiAgICAgICAgICAgICAgICAgIHN0b3BwaW5nX21ldHJpYyA9ICdBVUNQUicsCiAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2VuY29kaW5nID0gJ29uZV9ob3RfZXhwbGljaXQnKQoKIyByZl9wYXJhbXMgPC0gbGlzdChtYXhfZGVwdGggPSBjKDIwLCA1MCksCiMgICAgICAgICAgICAgICAgICAgYmFsYW5jZV9jbGFzc2VzID0gVFJVRSwKIyAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9lbmNvZGluZz0gJ29uZV9ob3RfZXhwbGljaXQnKQoKIyBkZWZpbmUgbnVtYmVyIG9mIGJvb3RzdHJhcHMKbl9ib290ID0gNTAKCnByX2RydWdfcmYgPC0gbW9kZWxfZmVhdHVyZV9zZWxlY3Rpb24oIlJGIix0cmFpbmluZ19mcmFtZSA9IHRyYWluaW5nX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkYXRpb25fZnJhbWUgPSB2YWxpZGF0aW9uX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGh5cGVyX3BhcmFtcyA9IHJmX3BhcmFtcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdXRjb21lID0gb3V0Y29tZSwgbiA9IG5fYm9vdCwgc2VlZD1zZWVkKQoKYGBgCgpUaGUgZm9sbG93aW5nIHRhYmxlIGRpc3BsYXlzIHRoZSBtZWFuIHBlcmZvcm1hbmNlIG1ldHJpY3MgZm9yIHRoZSBib290c3RyYXBwZWQgbW9kZWxzIG9uIHRoZSB2YWxpZGF0aW9uIHNldCwgcmVtb3ZpbmcgdmFsdWVzIGZvciB3aGljaCB0aGVyZSBhcmUgTkEuCgpgYGB7ciBldmFsdWF0ZSBib290c3RyYXAgbW9kZWwgcGVyZm9ybWFuY2UgcmYsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9Cm1lYW5fYnNfcmZfcGVyZiA8LSBnZXRfbWV0cmljX3NldF9mcm9tX3BlcmZzKHByX2RydWdfcmYkcGVyZnMpICU+JQogIGRwbHlyOjpzZWxlY3QoYWNjdXJhY3ksIG1wY2UsIHNlbnMsIHNwZWMsIHBwdiwgbnB2LCByb2NfYXVjLCBwcl9hdWMsCiAgICAgICAgICAgICAgICB0bnMsIHRwcywgZm5zLCBmcHMsIG5vX24sIG5vX3AsICBlcnJfcmF0ZSwgYmFsX2FjY3VyYWN5LCBldmVyeXRoaW5nKCkpICU+JQogIHN1bW1hcmlzZV9pZihpcy5udW1lcmljLCBtZWFuLCBuYS5ybT1UUlVFKSAlPiUKICBtdXRhdGUobW9kZWwgPSAnYnNfcmYnKSAlPiUKICBkcGx5cjo6c2VsZWN0KG1vZGVsLCBldmVyeXRoaW5nKCkpCgptZWFuX2JzX3JmX3BlcmYKYGBgCkFzIHNob3duLCB0aGUgYm9vdHN0cmFwcGVkIG1vZGVscyB0ZW5kIHRvIGhhdmUgaGlnaCBzcGVjaWZpY2l0eSBidXQgbG93IHNlbnNpdGl2aXR5LCBpbmRpY2F0aW5nIHRoYXQgdGhlcmUgaXMgYSBjaGFsbGVuZ2UgaW4gaWRlbnRpZnlpbmcgc3ViamVjdHMgd2l0aCBzdWljaWRhbCBpZGVhdGlvbi4KCiMjIyBGZWF0dXJlIGltcG9ydGFuY2VzOiBSYW5kb20gRm9yZXN0CiMjIyMgTWVhbiBkZWNyZWFzZSBpbiBpbXB1cml0eSAoTURJKQpgYGB7cn0KYm9vdF9yZl9tZGkgPC0gcHJfZHJ1Z19yZiRtZGkgJT4lCiAgZ2V0X21lZGlhbl9wbGFjZW1lbnQodXNlX2Jhc2VfdmFyID0gVFJVRSkgJT4lCiAgYWRkX2F0dHJpYnV0ZV9uYW1lcygncHJlZGljdG9yJywgZnVsbF9kYXRhc2V0KSAlPiUKICBkcGx5cjo6c2VsZWN0KHByZWRpY3RvciwgYXR0X25hbWUsIG92ZXJhbGxfcmFuaykKCmhlYWQoYm9vdF9yZl9tZGksIDIwKQpgYGAKVGhpcyB0YWJsZSByZXR1cm5zIHRoZSBNREkgdmFyaWFibGUgaW1wb3J0YW5jZSByYW5rcyB0aGF0IHJldHVybmVkIGZyb20gZWFjaCBvZiB0aGUgYm9vdHN0cmFwcGVkIG1vZGVscy4KCmBgYHtyIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMjV9CiMgTmVlZHMgdG8gYmUgZml4ZWQgc28gdGhhdCBheGVzIGRvbid0IG92ZXJsYXAgZWFjaCBvdGhlciBhbmQgb2JzY3VyZSB1bmRlcnN0YW5kaW5nCnBsb3RfcGxhY2VtZW50X2JveHBsb3QocHJfZHJ1Z19yZiRtZGkpCmBgYAoKIyMjIyBQZXJtdXRhdGlvbiBpbXBvcnRhbmNlCk5vdywgbGV0J3MgbG9vayBhdCB0aGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZToKCmBgYHtyIGdldCByZiBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpib290X3JmX3Blcm1fcGx0IDwtIHByX2RydWdfcmYkbW9kZWxzICU+JQogIGdldF9hZ2dyZWdhdGVkX3Blcm11dGVfaW1wKHRyYWluaW5nX2RmLCBvdXRjb21lPW91dGNvbWUpCmBgYAoKYGBge3IgYWdncmVnYXRlIHJmIHBlcm0gcmVzdWx0c30KbWV0IDwtICdwcl9hdWMnCmJvb3RfcmZfcGVybSA8LSBib290X3JmX3Blcm1fcGx0ICU+JQogIGdldF9wZXJtdXRlX3BsYWNlbWVudChtZXRyaWNfb2k9bWV0KSAlPiUKICBhZGRfYXR0cmlidXRlX25hbWVzKCdwcmVkaWN0b3InLCBmdWxsX2RhdGFzZXQpICU+JQogIGRwbHlyOjpzZWxlY3QocHJlZGljdG9yLCBldmVyeXRoaW5nKCkpCgpoZWFkKGJvb3RfcmZfcGVybSwgMjApCmBgYAoKIyMjIyBNREkgdnMgUGVybXV0YXRpb24gaW1wb3J0YW5jZQpJbiB0aGlzIHN0ZXAsIHdlIGFzc2VzcyB0aGUgZGlmZmVyZW5jZXMgZ2VuZXJhdGVkIGJldHdlZW4gdGhlIGRpZmZlcmVudCB0eXBlcyBvZiBpbXBvcnRhbmNlcy4KYGBge3IgZmlnLndpZHRoID0gMTYsIGZpZy5oZWlnaHQgPSAxNH0KY2JpbmQoYm9vdF9yZl9tZGlbMToyMCxdLCBkcGx5cjo6c2VsZWN0KGJvb3RfcmZfcGVybVsxOjIwLF0sIC1hbGxfb2YobWV0KSkpCmBgYApBcyBzaG93biwgdGhlIE1ESSBpbXBvcnRhbmNlIHN1ZmZlcnMgZnJvbSBpbWJhbGFuY2VzIGR1ZSB0byB0aGUgbnVtYmVyIG9mIHZhbHVlcyBhc3NvY2lhdGVkIHdpdGggYSBwcmVkaWN0b3IuICBCZWNhdXNlIHRoZSB3YXZlIGFnZXMgaGF2ZSBzbyBtYW55IG1vcmUgdmFsdWVzIHRoYW4gdGhlIG90aGVyIGZhY3RvcnMsIHRoaXMgYXJ0aWZpY2lhbGx5IGluZmxhdGVzIHRoZWlyIGltcG9ydGFuY2UgaW4gTURJLiAgVGhlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgaXMgbW9yZSBpbnR1aXRpdmUuCgpgYGB7ciBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEyfQpwbG90X3Blcm11dGVfdmFyX2ltcChib290X3JmX3Blcm0sIG1ldHJpYyA9IHByX2F1YykKYGBgCgoKIyMgTEFTU08gbW9kZWwKSW4gdGhpcyBzdGVwLCB3ZSBtb2RlbCB0aGUgcmVsYXRpb24gYmV0d2VlbiB0aGUgb3V0Y29tZXMgYW5kIHRoZSBwcmVkaWN0b3JzIHVzaW5nIGEgbGluZWFyIHJlZ3Jlc3Npb24gd2l0aCBMMiByZWd1bGFyaXphdGlvbi4gIFRoaXMgZHJpdmVzIHRoZSBpbXBvcnRhbmNlIG9mIHVuaW1wb3J0YW50IGFuZCByZWR1ZGFudCBmZWF0dXJlcyB0b3dhcmRzIHplcm8uCgpgYGB7ciBmZWF0dXJlIHNlbGVjdGlvbiBsYXNzbywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBGdW5jdGlvbiBwYXJhbWV0ZXJzCmxhc3NvX3BhcmFtcyA8LSBsaXN0KGFscGhhID0gYygxKSkKIyBDYWxsIG1vZGVsaW5nIGZ1bmN0aW9uIHVzaW5nIGZ1bmN0aW9uIHBhcmFtZXRlcnMgYW5kIHNob3cgdmlzdWFsaXphdGlvbiBvZiByZXN1bHRzLiAgUmVjb21tZW5kIHRoZSBudW1iZXIgb2YgZmVhdHVyZXMgdGhhdCBzaG91bGQgYmUgdXNlZC4gIFJlcG9ydCBwZXJmb3JtYW5jZSBtZXRyaWMgc3RhdHMuCgpwcl9kcnVnX2xhc3NvIDwtIG1vZGVsX2ZlYXR1cmVfc2VsZWN0aW9uKCAiTGFzc28iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpbmluZ19mcmFtZSA9IHRyYWluaW5nX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZGF0aW9uX2ZyYW1lID0gdmFsaWRhdGlvbl9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaHlwZXJfcGFyYW1zID0gbGFzc29fcGFyYW1zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdXRjb21lID0gb3V0Y29tZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4gPSBuX2Jvb3QpCmBgYAoKYGBge3IgZXZhbHVhdGUgYm9vdHN0cmFwIG1vZGVsIHBlcmZvcm1hbmNlIGxhc3NvLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQptZWFuX2JzX2xhc3NvX3BlcmYgPC0gZ2V0X21ldHJpY19zZXRfZnJvbV9wZXJmcyhwcl9kcnVnX2xhc3NvJHBlcmZzKSAlPiUKICBkcGx5cjo6c2VsZWN0KGFjY3VyYWN5LCBtcGNlLCBzZW5zLCBzcGVjLCBwcHYsIG5wdiwgcm9jX2F1YywgcHJfYXVjLAogICAgICAgICAgICAgICAgdG5zLCB0cHMsIGZucywgZnBzLCBub19uLCBub19wLCAgZXJyX3JhdGUsIGJhbF9hY2N1cmFjeSwgZXZlcnl0aGluZygpKSAlPiUKICBzdW1tYXJpc2VfaWYoaXMubnVtZXJpYywgbWVhbiwgbmEucm09VFJVRSkgJT4lIAogIG11dGF0ZShtb2RlbD0nYnNfbGFzc28nKSAlPiUKICBkcGx5cjo6c2VsZWN0KG1vZGVsLCBldmVyeXRoaW5nKCkpCgptZWFuX2JzX2xhc3NvX3BlcmYKYGBgCgojIyMgRmVhdHVyZSBpbXBvcnRhbmNlczogTEFTU08KIyMjIyBDb2VmZmljaWVudC1iYXNlZCB2YXJpYWJsZSBpbXBvcnRhbmNlCmBgYHtyfQpib290X2xhc3NvX21kaSA8LSBwcl9kcnVnX2xhc3NvJG1kaSAlPiUKICBnZXRfbWVkaWFuX3BsYWNlbWVudCh1c2VfYmFzZV92YXIgPSBUUlVFKSAlPiUKICBhZGRfYXR0cmlidXRlX25hbWVzKCdwcmVkaWN0b3InLCBmdWxsX2RhdGFzZXQpICU+JQogIGRwbHlyOjpzZWxlY3QocHJlZGljdG9yLCBhdHRfbmFtZSwgb3ZlcmFsbF9yYW5rKQoKaGVhZChib290X2xhc3NvX21kaSwgMjApCmBgYAoKYGBge3IgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAzNX0KcGxvdF9wbGFjZW1lbnRfYm94cGxvdChwcl9kcnVnX2xhc3NvJG1kaSkKYGBgCgojIyMjIFBlcm11dGF0aW9uIGltcG9ydGFuY2UKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KYm9vdF9sYXNzb19wZXJtX3BsdCA8LSBwcl9kcnVnX2xhc3NvJG1vZGVscyAlPiUKICBnZXRfYWdncmVnYXRlZF9wZXJtdXRlX2ltcCh0cmFpbmluZ19kZiwgb3V0Y29tZT1vdXRjb21lKQpgYGAKCmBgYHtyIGFnZ3JlZ2F0ZSBsYXNzbyBwZXJtdXRhdGlvbnMgYW5kIGdldCBtZXRyaWNzfQpib290X2xhc3NvX3Blcm0gPC0gYm9vdF9sYXNzb19wZXJtX3BsdCAlPiUKICBnZXRfcGVybXV0ZV9wbGFjZW1lbnQobWV0cmljX29pPW1ldCkgJT4lICNzZXQgaW4gcmFuZG9tIGZvcmVzdCBzZWN0aW9uCiAgYWRkX2F0dHJpYnV0ZV9uYW1lcygncHJlZGljdG9yJywgZnVsbF9kYXRhc2V0KSAlPiUKICBkcGx5cjo6c2VsZWN0KHByZWRpY3RvciwgZXZlcnl0aGluZygpKQoKaGVhZChib290X2xhc3NvX3Blcm0sIDIwKQpgYGAKCmBgYHtyIHBsb3QgbGFzc28gcGVybXV0YXRpb24sIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTJ9CnBsb3RfcGVybXV0ZV92YXJfaW1wKGJvb3RfbGFzc29fcGVybSwgbWV0cmljID0gcHJfYXVjKQpgYGAKCiMjIyMgQ29lZmZpY2llbnQgdnMuIFBlcm11dGF0aW9uIGltcG9ydGFuY2UKTm93LCB3ZSBjb21wYXJlIHRoZSBmZWF0dXJlIGltcG9ydGFuY2VzIGdlbmVyYXRlZCBieSB0aGUgdHdvIGRpZmZlcmVudCBhcHByb2FjaGVzLiAgVGhlIHRyYWRpdGlvbmFsIG1ldGhvZCBvZiBldmFsdWF0aW5nIGZlYXR1cmUgaW1wb3J0YW5jZSBmb3IgcmVncmVzc2lvbiBtZXRob2RzIGlzIHRocm91Z2ggYW5hbHlzaXMgb2YgdGhlIGNvZWZmaWNpZW50cy4KYGBge3IgZmlnLndpZHRoID0gMTYsIGZpZy5oZWlnaHQgPSAxNH0KY2JpbmQoYm9vdF9sYXNzb19tZGlbMToyMCxdLCBkcGx5cjo6c2VsZWN0KGJvb3RfbGFzc29fcGVybVsxOjIwLF0sIC1tZXQpKQpgYGAKCiMjIENvbXBhcmlzb246IE1vZGVsIFR5cGUgTWVhbiBQZXJmb3JtYW5jZQpUaGUgZm9sbG93aW5nIHRhYmxlIGNvbXBhcmVzIHRoZSBtZWFuIHBlcmZvcm1hbmNlIG9mIGJvb3RzdHJhcHBlZCByYW5kb20gZm9yZXN0cyB0byB0aGUgbWVhbiBwZXJmb3JtYW5jZSBvZiBib290c3RyYXBwZWQgTEFTU08gbWV0aG9kcy4KYGBge3J9CmJzX2NvbXBfcGVyZnMgPC0gcmJpbmQobWVhbl9ic19yZl9wZXJmLCBtZWFuX2JzX2xhc3NvX3BlcmYpIApic19jb21wX3BlcmZzCmBgYAoKIyMgQ29tcGFyaXNvbjogTW9kZWwgVHlwZSBGZWF0dXJlIEltcG9ydGFuY2UKSGVyZSwgd2UgbG9vayBhdCB0aGUgYWdncmVnYXRlZCByZXN1bHRzIG9mIHRoZSBib290c3RyYXBwZWQgcHJlZGljdG9ycyBhbmQgY29tcGFyZSB0aGUgbW9kZWxzIGdlbmVyYXRlZCB0byBlYWNoIG90aGVyLgpgYGB7cn0Kam9pbmVkX3Jlc3VsdHMgPC0gYm9vdF9yZl9wZXJtICU+JQogIGRwbHlyOjpzZWxlY3QoLW1ldCkgJT4lCiAgZnVsbF9qb2luKGRwbHlyOjpzZWxlY3QoYm9vdF9sYXNzb19wZXJtLCAtbWV0KSwgYnk9YygicHJlZGljdG9yIiwgImF0dF9uYW1lIiksIHN1ZmZpeD1jKCcucmYnLCAnLmxhc3NvJykpICU+JQogIG11dGF0ZShtZWFuX3JhbmsgPSAob3ZlcmFsbF9yYW5rLnJmK292ZXJhbGxfcmFuay5sYXNzbykvMikgJT4lCiAgYXJyYW5nZShtZWFuX3JhbmspCgpoZWFkKGpvaW5lZF9yZXN1bHRzLCAyMCkKYGBgCgpUaGUgZm9sbG93aW5nIHZpc3VhbGl6YXRpb24gcHJvdmlkZXMgdGhlIGludHVpdGlvbiBhYm91dCB0aGUgZGlmZmVyZW5jZXMgaW4gdGhlIHJhbmtpbmdzIGJldHdlZW4gbW9kZWwgdHlwZXMuICBUaGV5J3JlIG9yZGVyZWQgYnkgdGhlIG92ZXJhbGwgbWVhbiBpbXBvcnRhbmNlLCBhbmQgZm9yIGEgZ2l2ZW4gdmFyaWFibGUsIHRoZSBkaWZmZXJlbmNlcyBpbiByYW5rIGFyZSBzaG93bi4KYGBge3IgZmlnLndpZHRoID0gMTYsIGZpZy5oZWlnaHQgPSAxNH0KIyBDb21wYXJpc29uIG9mIHRvcF9uIGZlYXR1cmVzCmpvaW5lZF9yZXN1bHRzICU+JQogIGNvbXBhcmVfZmVhdHVyZV9zZWxlY3QoaW50ZXJhY3RpdmUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgdG9wX24gPSAxMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICBvcGFjaXR5ID0gMC41MCwKICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfdGl0bGUgPSAiUGVybXV0YXRpb24gSW1wb3J0YW5jZSBvZiBQcmVkaWN0b3JzIGJ5IE1vZGVsIikKYGBgCgojIEdlbmVyYXRpb24gb2YgZmluYWwgbW9kZWwgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9CgojIyBSRiBtb2RlbApJbiB0aGlzIHN0ZXAsIHdlIGJ1aWxkIHRoZSBmaW5hbCBtb2RlbCBmb3IgdGhlIHJhbmRvbSBmb3Jlc3QuICBXZSB1c2Ugc2xpZ2h0bHkgbW9yZSB2YWx1ZXMgaW4gb3JkZXIgdG8gY29tZSB1cCB3aXRoIHRoZSBiZXN0IG1vZGVsLCBrZWVwaW5nIGluIG1pbmQgdGhlIG51bWJlciBvZiBjb21iaW5hdGlvbnMgdGhhdCBhcmUgcmVxdWlyZWQgdG8gcnVuIHRvIGV2YWx1YXRlIHRoZSBncmlkLgpgYGB7ciBmaW5hbCBtb2RlbCBldmFsdWF0aW9uIHJmfQoKIyAjIFNwYW5zIG9mIGh5cGVyIHBhcmFtZXRlcnMgZm9yIHJhbmRvbSBmb3Jlc3QKcmZfcGFyYW1zIDwtIGxpc3QobWF4X2RlcHRoID0gNTAsCiAgICAgICAgICAgICAgICAgIG50cmVlcyA9IDE1MCwKICAgICAgICAgICAgICAgICAgbXRyaWVzID0gc2VxKC0xLCAzMCwgYnk9NSksCiAgICAgICAgICAgICAgICAgIG1pbl9yb3dzID0gc2VxKDUsIDYwLCBieT01KSwKICAgICAgICAgICAgICAgICAgYmFsYW5jZV9jbGFzc2VzID0gYyhUUlVFLCBGQUxTRSksCiAgICAgICAgICAgICAgICAgIHN0b3BwaW5nX21ldHJpYyA9ICdBVUNQUicsCiAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2VuY29kaW5nID0gJ29uZV9ob3RfZXhwbGljaXQnKQoKIyByZl9wYXJhbXMgPC0gbGlzdChtYXhfZGVwdGggPSBzZXEoMjAsIDUwLCAyMCksCiMgICAgICAgICAgICAgICAgICAgYmFsYW5jZV9jbGFzc2VzID0gVFJVRSwKIyAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9lbmNvZGluZz0gJ29uZV9ob3RfZXhwbGljaXQnKQoKIyBGdW5jdGlvbiBwYXJhbWV0ZXJzCmZpbmFsX21vZGVsX3JmIDwtIHJmX21vZGVsKG91dGNvbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWluaW5nX2ZyYW1lID0gdHJhaW5pbmdfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkYXRpb25fZnJhbWUgPSB2YWxpZGF0aW9uX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZm9sZHMgPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICBoeXBlcl9wYXJhbXMgPSByZl9wYXJhbXMsIG1vZGVsX3NlZWQ9c2VlZCkKCmBgYAoKIyMjIFBlcmZvcm1hbmNlClRoZSBmaW5hbCByYW5kb20gZm9yZXN0IHBlcmZvcm1hbmNlIG1ldHJpY3MgYXJlIHNob3duIGJlbG93OgpgYGB7cn0KIyBzaG93IG1vZGVsIGZpbmFsIHBlcmZvcm1hbmNlCnByaW50KGZpbmFsX21vZGVsX3JmW1syXV0pCmBgYAoKIyMjIEZlYXR1cmVzOiBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlCmBgYHtyIGdldCBwbG90dGFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZmluYWxfcmZfcGVybV9wbHQgPC0gYyhmaW5hbF9tb2RlbF9yZltbMV1dKSAlPiUKICBnZXRfYWdncmVnYXRlZF9wZXJtdXRlX2ltcCh0cmFpbmluZ19kZiwgb3V0Y29tZT1vdXRjb21lKQpgYGAKCmBgYHtyIGdldCBtZXRyaWNzIGZvciBmaW5hbCByZn0KZmluYWxfcmZfcGVybSA8LSBmaW5hbF9yZl9wZXJtX3BsdCAlPiUKICBnZXRfcGVybXV0ZV9wbGFjZW1lbnQobWV0cmljX29pPW1ldCkgJT4lCiAgYWRkX2F0dHJpYnV0ZV9uYW1lcygncHJlZGljdG9yJywgZnVsbF9kYXRhc2V0KSAlPiUKICBkcGx5cjo6c2VsZWN0KHByZWRpY3RvciwgZXZlcnl0aGluZygpKQoKaGVhZChmaW5hbF9yZl9wZXJtLCAyMCkKYGBgCgpgYGB7ciBwbG90IHJmIGZpbmFsIHBlcm11dGF0aW9uLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEyfQpwbG90X3Blcm11dGVfdmFyX2ltcChmaW5hbF9yZl9wZXJtLCBtZXRyaWMgPSBwcl9hdWMpCmBgYAojIyMgQ29tcGFyaXNvbiB3aXRoIGJvb3RzdHJhcCByZXN1bHRzClRoaXMgc2VjdGlvbiBpbnZlc3RpZ2F0ZXMgdGhlIGRpZmZlcmVuY2VzIGluIHRoZSBib290c3RyYXAgcmVzdWx0cyB2cyB0aGUgZmVhdHVyZXMgZ2VuZXJhdGVkIGZyb20gdGhlIHJhbmRvbSBmb3Jlc3QgZmluYWwgbW9kZWwuICBUaGUgZm9sbG93aW5nIHRhYmxlIHNob3dzIHRoZSBvdmVyYWxsIGRpZmZlcmVuY2VzIGluIHJhbmsuCgpgYGB7cn0KcmZfam9pbmVkX3Jlc3VsdHMgPC0gZmluYWxfcmZfcGVybSAlPiUKICBkcGx5cjo6c2VsZWN0KC1tZXQpICU+JQogIGZ1bGxfam9pbihkcGx5cjo6c2VsZWN0KGJvb3RfcmZfcGVybSwgLW1ldCksIGJ5PWMoInByZWRpY3RvciIsICJhdHRfbmFtZSIpLCBzdWZmaXg9YygnLmZpbmFsJywgJy5ib290c3RyYXAnKSkgJT4lCiAgbXV0YXRlKG1lYW5fcmFuayA9IChvdmVyYWxsX3JhbmsuZmluYWwgKyBvdmVyYWxsX3JhbmsuYm9vdHN0cmFwKS8yKSAlPiUKICBhcnJhbmdlKG1lYW5fcmFuaykKCmhlYWQocmZfam9pbmVkX3Jlc3VsdHMsIDIwKQpgYGAKClRoZSBmb2xsb3dpbmcgcGxvdCBwcm92aWRlcyB2aXN1YWxpemF0aW9ucyBmb3IgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIGZpbmFsIG1vZGVsIHJhbmtpbmdzIHZzIHRoZSBib290c3RyYXAuCgpgYGB7ciBmaWcuaGVpZ2h0PTE0LCBmaWcud2lkdGg9MTZ9CiMgQ29tcGFyaXNvbiBvZiB0b3BfbiBmZWF0dXJlcwpyZl9qb2luZWRfcmVzdWx0cyAlPiUKICBjb21wYXJlX2ZlYXR1cmVfc2VsZWN0KHNlbF9jb2xzID0gYygib3ZlcmFsbF9yYW5rLmZpbmFsIiwgIm92ZXJhbGxfcmFuay5ib290c3RyYXAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGludGVyYWN0aXZlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgIHRvcF9uID0gMTAwLAogICAgICAgICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDAuNTAsCiAgICAgICAgICAgICAgICAgICAgICAgICBwbG90X3RpdGxlID0gIlBlcm11dGF0aW9uIEltcG9ydGFuY2Ugb2YgUHJlZGljdG9yczogRmluYWwgdnMuIEJvb3RzdHJhcCIpCmBgYAoKIyMgTEFTU08gbW9kZWwKTm93LCB3ZSBjcmVhdGUgdGhlIGZpbmFsIG1vZGVsIGZvciBMQVNTTy4gIFRoZXJlIGlzIG5vIHN1YnN0YW50aWFsIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGlzIG1ldGhvZCBhbmQgdGhlIGJvb3RzdHJhcCBtZXRob2RzLCBvdGhlciB0aGFuIHRoZSBkYXRhIHVwb24gd2hpY2ggdGhlIG1vZGVsIGlzIGJlaW5nIGJ1aWx0LgpgYGB7ciBmaW5hbCBtb2RlbCBldmFsdWF0aW9uIGxhc3NvLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIEZ1bmN0aW9uIHBhcmFtZXRlcnMKbGFzc29fcGFyYW1zIDwtIGxpc3QoYWxwaGEgPSBjKDEpKQoKZmluYWxfbW9kZWxfbGFzc28gPC0gbGFzc29fbW9kZWwodHJhaW5pbmdfZnJhbWUgPSB0cmFpbmluZ19kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRhdGlvbl9mcmFtZSA9IHZhbGlkYXRpb25fZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG91dGNvbWUgPSBvdXRjb21lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZm9sZHMgPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoeXBlcl9wYXJhbXMgPSBsYXNzb19wYXJhbXMpCmBgYAoKVGhlIGZpbmFsIExBU1NPIHBlcmZvcm1hbmNlIG1ldHJpY3MgYXJlIHNob3duIGJlbG93OgpgYGB7cn0KIyBzaG93IG1vZGVsIGZpbmFsIHBlcmZvcm1hbmNlCnByaW50KGZpbmFsX21vZGVsX2xhc3NvW1syXV0pCmBgYAoKIyMjIEZlYXR1cmVzOiBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlCmBgYHtyIGdldCBwbG90dGFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSBsYXNzbywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZmluYWxfbGFzc29fcGVybV9wbHQgPC0gYyhmaW5hbF9tb2RlbF9sYXNzb1tbMV1dKSAlPiUKICBnZXRfYWdncmVnYXRlZF9wZXJtdXRlX2ltcCh0cmFpbmluZ19kZiwgb3V0Y29tZT1vdXRjb21lKQpgYGAKCmBgYHtyIGdldCBwZXJtdXRlIGZlYXR1cmVzIGZvciBmaW5hbCBsYXNzb30KZmluYWxfbGFzc29fcGVybSA8LSBmaW5hbF9sYXNzb19wZXJtX3BsdCAlPiUKICBnZXRfcGVybXV0ZV9wbGFjZW1lbnQobWV0cmljX29pPW1ldCkgJT4lCiAgYWRkX2F0dHJpYnV0ZV9uYW1lcygncHJlZGljdG9yJywgZnVsbF9kYXRhc2V0KSAlPiUKICBkcGx5cjo6c2VsZWN0KHByZWRpY3RvciwgZXZlcnl0aGluZygpKQoKaGVhZChmaW5hbF9sYXNzb19wZXJtLCAyMCkKYGBgCgpgYGB7ciBwbG90IGxhc3NvIGZpbmFsIHBlcm11dGF0aW9uLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEyfQpwbG90X3Blcm11dGVfdmFyX2ltcChmaW5hbF9sYXNzb19wZXJtLCBtZXRyaWMgPSBwcl9hdWMpCmBgYAojIyMgQ29tcGFyaXNvbiB3aXRoIGJvb3RzdHJhcCByZXN1bHRzClRoaXMgc2VjdGlvbiBpbnZlc3RpZ2F0ZXMgdGhlIGRpZmZlcmVuY2VzIGluIHRoZSBib290c3RyYXAgcmVzdWx0cyB2cyB0aGUgZmVhdHVyZXMgZ2VuZXJhdGVkIGZyb20gdGhlIExBU1NPIGZpbmFsIG1vZGVsLiAgVGhlIGZvbGxvd2luZyB0YWJsZSBzaG93cyB0aGUgb3ZlcmFsbCBkaWZmZXJlbmNlcyBpbiByYW5rLgoKYGBge3J9Cmxhc3NvX2pvaW5lZF9yZXN1bHRzIDwtIGZpbmFsX2xhc3NvX3Blcm0gJT4lCiAgZHBseXI6OnNlbGVjdCgtbWV0KSAlPiUKICBmdWxsX2pvaW4oZHBseXI6OnNlbGVjdChib290X2xhc3NvX3Blcm0sIC1tZXQpLCBieT1jKCJwcmVkaWN0b3IiLCAiYXR0X25hbWUiKSwgc3VmZml4PWMoJy5maW5hbCcsICcuYm9vdHN0cmFwJykpICU+JQogIG11dGF0ZShtZWFuX3JhbmsgPSAob3ZlcmFsbF9yYW5rLmZpbmFsICsgb3ZlcmFsbF9yYW5rLmJvb3RzdHJhcCkvMikgJT4lCiAgYXJyYW5nZShtZWFuX3JhbmspCgpoZWFkKGxhc3NvX2pvaW5lZF9yZXN1bHRzLCAyMCkKYGBgCgpUaGUgZm9sbG93aW5nIHBsb3QgcHJvdmlkZXMgdmlzdWFsaXphdGlvbnMgZm9yIHRoZSBkaWZmZXJlbmNlIGluIHRoZSBmaW5hbCBtb2RlbCByYW5raW5ncyB2cyB0aGUgYm9vdHN0cmFwLgoKYGBge3IgZmlnLndpZHRoID0gMTYsIGZpZy5oZWlnaHQgPSAxNH0KIyBDb21wYXJpc29uIG9mIHRvcF9uIGZlYXR1cmVzCmxhc3NvX2pvaW5lZF9yZXN1bHRzICU+JQogIGNvbXBhcmVfZmVhdHVyZV9zZWxlY3Qoc2VsX2NvbHMgPSBjKCJvdmVyYWxsX3JhbmsuZmluYWwiLCAib3ZlcmFsbF9yYW5rLmJvb3RzdHJhcCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJhY3RpdmUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgdG9wX24gPSAxMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICBvcGFjaXR5ID0gMC41MCwKICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfdGl0bGUgPSAiUGVybXV0YXRpb24gSW1wb3J0YW5jZSBvZiBQcmVkaWN0b3JzOiBGaW5hbCB2cy4gQm9vdHN0cmFwIikKYGBgCgojIyBDb21wYXJpc29uOiBGaW5hbCBtb2RlbCBmZWF0dXJlcwpIZXJlLCB3ZSBjb21wYXJlIHRoZSBmZWF0dXJlcyBnZW5lcmF0ZWQgYnkgdGhlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgYmV0d2VlbiB0aGUgdHdvIGZpbmFsIG1vZGVscy4KCmBgYHtyfQpyZl9sYXNzb19maW5hbF9qb2luZWRfcmVzdWx0cyA8LSBmaW5hbF9yZl9wZXJtICU+JQogIGRwbHlyOjpzZWxlY3QoLW1ldCkgJT4lCiAgZnVsbF9qb2luKGRwbHlyOjpzZWxlY3QoZmluYWxfbGFzc29fcGVybSwgLW1ldCksIGJ5PWMoInByZWRpY3RvciIsICJhdHRfbmFtZSIpLCBzdWZmaXg9YygnLnJmJywgJy5sYXNzbycpKSAlPiUKICBtdXRhdGUobWVhbl9yYW5rID0gKG92ZXJhbGxfcmFuay5yZitvdmVyYWxsX3JhbmsubGFzc28pLzIpICU+JQogIGFycmFuZ2UobWVhbl9yYW5rKQoKaGVhZChyZl9sYXNzb19maW5hbF9qb2luZWRfcmVzdWx0cywgMjApCmBgYAoKVGhlIGZvbGxvd2luZyB2aXN1YWxpemF0aW9uIHByb3ZpZGVzIHRoZSBpbnR1aXRpb24gYWJvdXQgdGhlIGRpZmZlcmVuY2VzIGluIHRoZSByYW5raW5ncyBiZXR3ZWVuIHRoZSBmaW5hbCBtb2RlbCB0eXBlcy4gIFRoZXkncmUgb3JkZXJlZCBieSB0aGUgb3ZlcmFsbCBtZWFuIGltcG9ydGFuY2UsIGFuZCBmb3IgYSBnaXZlbiB2YXJpYWJsZSwgdGhlIGRpZmZlcmVuY2VzIGluIHJhbmsgYXJlIHNob3duLgoKYGBge3IgZmlnLndpZHRoID0gMTYsIGZpZy5oZWlnaHQgPSAxNH0KIyBDb21wYXJpc29uIG9mIHRvcF9uIGZlYXR1cmVzCnJmX2xhc3NvX2ZpbmFsX2pvaW5lZF9yZXN1bHRzICU+JQogIGNvbXBhcmVfZmVhdHVyZV9zZWxlY3Qoc2VsX2NvbHMgPSBjKCJvdmVyYWxsX3JhbmsucmYiLCAib3ZlcmFsbF9yYW5rLmxhc3NvIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBpbnRlcmFjdGl2ZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICB0b3BfbiA9IDEwMCwKICAgICAgICAgICAgICAgICAgICAgICAgIG9wYWNpdHkgPSAwLjUwLAogICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF90aXRsZSA9ICJQZXJtdXRhdGlvbiBJbXBvcnRhbmNlIG9mIFByZWRpY3RvcnM6IFJhbmRvbSBGb3Jlc3QgdnMgTGFzc28iKQpgYGAKCiMjIENvbXBhcmlzb246IEZpbmFsIG1vZGVsIHBlcmZvcm1hbmNlCldpdGggdGhlIGZpbmFsIG1vZGVscyBnZW5lcmF0ZWQsIHdlJ3JlIG5vdyBhYmxlIHRvIGNvbXBhcmUgdGhlaXIgcGVyZm9ybWFuY2UgbWV0cmljcy4KYGBge3IgZmluYWwgbW9kZWwgY29tcGFyaXNvbiwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMn0KIyBDb21wYXJpc29uIG9mIHBlcmZvcm1hbmNlIG1ldHJpY3MKdmFsaWRfcGVyZiA8LSBnZXRfbWV0cmljX3NldF9mcm9tX3BlcmZzKHBlcmZfbGlzdCA9IGxpc3QoZmluYWxfbW9kZWxfcmZbWzJdXSwgZmluYWxfbW9kZWxfbGFzc29bWzJdXSkpICU+JQogIG11dGF0ZShtb2RlbCA9IGMoJ3JmJywgJ2xhc3NvJykpCgp0ZXN0aW5nX3BlcmYgPC0gZ2V0X21ldHJpY19zZXRfZnJvbV9tb2RlbHModGVzdGluZ19kZiwgbGlzdChmaW5hbF9tb2RlbF9yZltbMV1dLCBmaW5hbF9tb2RlbF9sYXNzb1tbMV1dKSwgb3V0PW91dGNvbWUpICU+JQogIG11dGF0ZShtb2RlbCA9IGMoJ3JmJywgJ2xhc3NvJykpCmBgYAoKKipWYWxpZGF0aW9uIGFuZCBzZWxlY3Rpb24uKioKVGhlIGZvbGxvd2luZyB0YWJsZSBzaG93cyB0aGUgY29tcGFyaXNvbiBiZXR3ZWVuIG1vZGVscyBpbiB0ZXJtcyBvZiB0aGUgdmFsaWRhdGlvbiBzZXQuICBXZSBjYW4gc2VsZWN0IG91ciBmaW5hbCBtb2RlbCBiYXNlZCBvbiB0aGUgYmVzdCBwZXJmb3JtaW5nIG1vZGVsIGFjY29yZGluZyB0byB0aGUgbWV0cmljLgpgYGB7cn0KcHJpbnQodmFsaWRfcGVyZikKYGBgCgoqKlRlc3RpbmcgcGVyZm9ybWFuY2UuKioKVGhlIGZvbGxvd2luZyBzaG93cyB0aGUgcGVyZm9ybWFuY2Ugb2YgYm90aCB0aGUgbW9kZWxzIG9uIHRoZSB0ZXN0IHNldC4gIE5vdGUgdGhhdCBhbHRob3VnaCB3ZSBkb24ndCB1c2UgdGhpcyB0ZXN0IHNldCB0byBldmFsdWF0ZSB0aGUgZmluYWwgbW9kZWxzLCB3ZSBjYW4gc3RpbGwgc2VlIGhvdyBvdXIgc2VsZWN0ZWQgbWV0aG9kIHdvdWxkIGhhdmUgcGVyZm9ybWVkLgpgYGB7cn0KcHJpbnQodGVzdGluZ19wZXJmKQpgYGAKClRoZSBmb2xsb3dpbmcgcGxvdHMgc2hvdyBhIGNvbXBhcmlzb24gYmV0d2VlbiB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIG1vZGVscyBvbiB0aGUgdmFsaWRhdGlvbiBhbmQgdGVzdCBzZXRzLiAgQWdhaW4sIHdlIGRvbid0IGNob29zZSB0aGUgbW9kZWwgYmFzZWQgb24gdGhlIHRlc3Qgc2V0LCBidXQgY3VyaW9zaXR5IGRpY3RhdGVzIHRoYXQgd2UgdmlldyB0aGlzIHBlcmZvcm1hbmNlLgoKYGBge3IgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA2fQojIFNob3cgcGxvdHMgc2lkZSBieSBzaWRlCm1ldHJpY3Nfb2ZfaW50ZXJlc3QgPSBjKCdtb2RlbCcsICdhY2N1cmFjeScsICdiYWxfYWNjdXJhY3knLCAnbXBjZScsICdzZW5zJywgJ3NwZWMnLCAncHB2JywgJ25wdicsICdwcl9hdWMnLCAncm9jX2F1YycpCnZhbGlkX3BsdCA8LSBwbG90X21ldHJpY19zZXQoZHBseXI6OnNlbGVjdCh2YWxpZF9wZXJmLCBhbGxfb2YobWV0cmljc19vZl9pbnRlcmVzdCkpLCBwbG90X3RpdGxlID0gIk1vZGVsIGNvbXBhcmlzb24gZm9yIHZhbGlkYXRpb24gc2V0IikKdGVzdF9wbHQgPC0gcGxvdF9tZXRyaWNfc2V0KGRwbHlyOjpzZWxlY3QodGVzdGluZ19wZXJmLCBhbGxfb2YobWV0cmljc19vZl9pbnRlcmVzdCkpLCBwbG90X3RpdGxlID0gIk1vZGVsIGNvbXBhcmlzb24gZm9yIHRlc3Rpbmcgc2V0IikKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JpZEV4dHJhOjphcnJhbmdlR3JvYih2YWxpZF9wbHQsIHRlc3RfcGx0LCBuY29sPTIsIG5yb3c9MSkpCmBgYAoKIyBPdXRjb21lIHZhcmlhYmxlIGRpc2N1c3Npb24KSGVyZSwgdGhlIHN1YmplY3QgbWF0dGVyIGV4cGVydHMgd2lsbCBjb21tZW50IG9uIHRoZSB0aGUgZGlmZmVyZW5jZXMgaW4gdGhlIGZlYXR1cmVzIG9idGFpbmVkIGJldHdlZW4gdGhlIHN0dWRpZWQgb3V0Y29tZXMgdmFyaWFibGVzIGFuZCBkaXNjdXNzIHRoZSBkaXNjcmVwYW5jaWVzIGFuZC9vciBjb2hlc2lvbi4KCmBgYHtyIG91dGNvbWUgdmFyaWFibGUgY29tcGFyaXNvbn0KIyBTaG93IGRpZmZlcmVuY2VzIGluIGZlYXR1cmVzIG9idGFpbmVkCgpgYGAKCg==